import json
import logging
from datetime import datetime
from django.contrib.auth.models import User
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db import transaction
from django.http import HttpResponse
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.authentication import TokenAuthentication
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from api_test.common import const
from api_test.common.api_response import JsonResponse
from api_test.common.common import record_dynamic
from api_test.common.decorator import catch_exception
from api_test.common.run import Run
from api_test.common_exception.exceptions import ParamsTypeErrorException, ParamsMissedException, \
    ObjectNotFoundException, NoPerMissionException, ObjectIsDeletedException, ParamsIsNullException
from api_test.models import Project, TestPlan, ApiCaseInfo, ApiInfo, TestPlanCase, TestPlanExecuteCaseReport, \
    TestPlanConf, ExecutePlanInfo
from api_test.serializers import TestPlanSerializer, TestPlanDeserializer, TestPlanCaseDeserializer, \
    TestPlanCaseSerializer, TestPlanExecuteCaseReportSerializer, TestPlanConfDeserializer, TestPlanConfSerializer, \
    TestPlanExecuteCaseReportListSerializer
from api_test.testdb.test_plan_case import TestPlanCase as DbPlanCase

logger = logging.getLogger(__name__)  # 这里使用 __name__ 动态搜索定义的 logger 配置，这里有一个层次关系的知识点。


# 校验项目是否存在/是否禁用/操作员是否有权限
@catch_exception
def check_object(data, request):
    project_id = data.get('project_id')
    project = Project.objects.filter(id=project_id)
    if not project:
        raise ObjectNotFoundException('项目' + str(project_id))
    project_obj = project.filter(status=True)
    if not project_obj:
        raise ObjectIsDeletedException('项目' + str(project_id))
    if not request.user.is_superuser:
        raise NoPerMissionException(request.user.username)


class TestPlanList(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(name='project_id', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='项目id'),
        openapi.Parameter(name='page', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='页数'),
        openapi.Parameter(name='page_size', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER,
                          description='一页显示个数'),
        openapi.Parameter(name='name', in_=openapi.IN_QUERY, type=openapi.TYPE_STRING,
                          description='测试计划名称'),
        openapi.Parameter(name='plan_id', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER,
                          description='测试计划id'),
    ])
    @catch_exception
    def get(self, request):
        """
        获取测试计划列表
        """
        try:
            page_size = int(request.GET.get('page_size', 20))
            current_page = int(request.GET.get('page', 1))
        except (TypeError, ValueError):
            return JsonResponse(code='999985', msg='page and page_size must be integer！')
        project_id = request.GET.get('project_id')
        name = request.GET.get('name')
        if not project_id:
            raise ParamsMissedException('project_id')
        plan_id = request.GET.get('plan_id')
        if not project_id.isdecimal():
            raise ParamsTypeErrorException('project_id')
        pro_data = Project.objects.filter(id=project_id)
        if not pro_data:
            raise ObjectNotFoundException('项目' + str(project_id))
        project_obj = pro_data.filter(status=True)
        if not project_obj:
            raise ObjectIsDeletedException('项目' + str(project_id))
        if plan_id:
            obi = TestPlan.objects.filter(project_id=project_id, id=plan_id)
            if not obi:
                raise ObjectNotFoundException('测试计划' + str(plan_id))
            if not obi.filter(status=True):
                raise ObjectIsDeletedException(f'测试计划{plan_id}')
            serialize = TestPlanSerializer(obi, many=True)
            return JsonResponse(data={'planList': serialize.data}, code='999999', msg='成功')
        if name:
            obi = TestPlan.objects.filter(project_id=project_id, name=name)
            if not obi:
                raise ObjectNotFoundException('测试计划' + name)
            if not obi.filter(status=True):
                raise ObjectIsDeletedException('测试计划' + name)
            serialize = TestPlanSerializer(obi, many=True)
            return JsonResponse(data={'planList': serialize.data}, code='999999', msg='成功')
        else:
            obi = TestPlan.objects.filter(project_id=project_id, status=True).order_by('id')
        paginator = Paginator(obi, page_size)  # paginator对象
        page_sizes = paginator.num_pages  # 总页数
        total = paginator.count  # 总数
        try:
            obm = paginator.page(current_page)
        except PageNotAnInteger:
            obm = paginator.page(1)
        except EmptyPage:
            obm = paginator.page(paginator.num_pages)
        serialize = TestPlanSerializer(obm, many=True)
        return JsonResponse(data={'planList': serialize.data,
                                  'currentPage': current_page,
                                  'total': total,
                                  'pageSizes': page_sizes,
                                  }, code='999999', msg='成功！')


class AddTestPlan(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['project_id', 'name']:
            if not data.get(param):
                raise ParamsMissedException(param)
        if not isinstance(data.get('project_id'), int):
            raise ParamsTypeErrorException('project_id')
        if not isinstance(data.get('name'), str):
            raise ParamsTypeErrorException('name')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['project_id', 'name'],
        properties={
            'project_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='项目id'),
            'name': openapi.Schema(type=openapi.TYPE_STRING, description='测试计划名称'),
            'caseList': openapi.Schema(type=openapi.TYPE_ARRAY,
                                       items=openapi.Schema(type=openapi.TYPE_OBJECT, required=['index', 'caseId'],
                                                            properties={
                                                                'index': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                                                        description='用例执行顺序index'),
                                                                'caseId': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                                                         description='用例id')
                                                            }),
                                       description='用例列表'),
            'description': openapi.Schema(type=openapi.TYPE_STRING, description='项目描述')
        }
    ))
    @catch_exception
    def post(self, request):
        """
        添加测试计划
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        obj = check_object(data, request)
        if obj:
            return obj
        case_list = data.get('caseList', [])
        case_info_list = []
        if case_list:
            new_case_list = [case.get('caseId') for case in case_list]
            if len(set(new_case_list)) < len(new_case_list):
                return JsonResponse(code='999995', msg='caseList中存在重复的caseId!')
            index_list = [case.get('index') for case in case_list]
            if len(set(index_list)) < len(index_list):
                return JsonResponse(code='999995', msg='caseList中存在重复的index!')
            case_obj = ApiCaseInfo.objects.filter(id__in=new_case_list, status=True)
            if case_obj.count() < len(case_list):
                return JsonResponse(code='999995', msg='caseList中存在禁用or不存在的caseId!')
            for case in case_obj:
                for i in case_list:
                    if i.get('caseId') == case.id:
                        api = ApiInfo.objects.get(id=case.api_id)
                        header = {}
                        if len(eval(case.header)) > 0:
                            for h in eval(case.header):
                                h.pop('_XID')
                                header[f'{h["key"]}'] = h["value"]
                        case_info_list.append({'index': i['index'], 'name': case.name, 'description': case.description,
                                               'request': json.dumps({'url': api.apiAddress, 'body': eval(case.data),
                                                                      'header': header, 'method': api.requestType
                                                                      }), 'case_id': case.id,
                                               'responseAssert': '[]',
                                               'variables': '[]',
                                               'globalVariable': '[]',
                                               'protocol': api.protocol
                                               })
        project_id = data.get('project_id')
        plan = TestPlan.objects.filter(name=data.get('name'), project_id=project_id)
        if plan:
            if plan.filter(status=True):
                return JsonResponse(code='999997', msg='测试计划name重复!')
            elif plan.filter(status=False):
                plan.delete()
        with transaction.atomic():
            serialize = TestPlanDeserializer(data=data)
            if case_info_list:
                case_serialize = TestPlanCaseDeserializer(data=case_info_list, many=True)
                if serialize.is_valid() and case_serialize.is_valid():
                    serialize.save(project=Project.objects.get(id=project_id),
                                   createUser=User.objects.get(id=request.user.pk),
                                   updateUser=User.objects.get(id=request.user.pk))
                    case_serialize.save(plan=TestPlan.objects.get(id=serialize.data.get('id')),
                                        createUser=User.objects.get(id=request.user.pk),
                                        updateUser=User.objects.get(id=request.user.pk))
                    record_dynamic(project=project_id, _type='新增', operationObject='测试计划',
                                   user=request.user.pk, data=f"新增测试计划{data.get('name')}")
                    return JsonResponse(data={'planId': serialize.data.get('id'), 'caseList': case_serialize.data},
                                        code='999999', msg='成功！')
                else:
                    error = serialize.errors if serialize.errors else case_serialize.errors
                    return JsonResponse(code='999995', msg=error)
            if serialize.is_valid():
                serialize.save(project=Project.objects.get(id=project_id),
                               createUser=User.objects.get(id=request.user.pk),
                               updateUser=User.objects.get(id=request.user.pk))
                record_dynamic(project=project_id, _type='新增', operationObject='测试计划',
                               user=request.user.pk, data=f"新增测试计划{data.get('id')}")
                return JsonResponse(data={'planId': serialize.data.get('id')},
                                    code='999999', msg='成功!')
            return JsonResponse(data='999995', msg=serialize.errors)


class UpdateTestPlan(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['project_id', 'id']:
            if not data.get(param):
                raise ParamsMissedException(param)
            if not isinstance(data.get(param), int):
                raise ParamsTypeErrorException(param)
        if not data.get('name'):
            raise ParamsMissedException('name')
        if not isinstance(data.get('name'), str):
            raise ParamsTypeErrorException('name')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['id', 'project_id', 'name'],
        properties={
            'id': openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划id'),
            'project_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='项目id'),
            'name': openapi.Schema(type=openapi.TYPE_STRING, description='测试计划名称'),
            'description': openapi.Schema(type=openapi.TYPE_STRING, description='测试计划描述')
        },
    ))
    @catch_exception
    def post(self, request):
        """
        修改测试计划
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        obj = check_object(data, request)
        if obj:
            return obj
        project_id = data.get('project_id')
        plan = TestPlan.objects.filter(id=data.get('id'), project_id=project_id)
        if not plan:
            raise ObjectNotFoundException('测试计划' + str(data.get('id')))
        if plan.filter(status=False):
            raise ObjectIsDeletedException('测试计划' + str(data.get('id')))
        count = TestPlan.objects.exclude(id=data.get('id')).filter(name=data.get('name'))
        if count:
            return JsonResponse(code='999997', msg='存在相同名称!')
        serialize = TestPlanDeserializer(data=data)
        if serialize.is_valid():
            serialize.update(instance=plan[0], validated_data=data)
            record_dynamic(project=data['project_id'],
                           _type='修改', operationObject='测试计划', user=request.user.pk,
                           data='修改测试计划' + data['name'])
            return JsonResponse(code='999999', msg='success', data={'id': data.get('id')})


class DelTestPlan(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['project_id', 'idList']:
            if not data.get(param):
                raise ParamsMissedException(param)
        if not isinstance(data.get('project_id'), int):
            raise ParamsTypeErrorException('project_id')
        if not isinstance(data.get('idList'), list):
            raise ParamsTypeErrorException('idList')
        for pid in data.get('idList'):
            if not isinstance(pid, int):
                raise ParamsTypeErrorException('idList列表选项')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['project_id', 'idList'],
        properties={
            'project_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='项目id'),
            'idList': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                                     description='测试计划id')
        },
    ))
    @catch_exception
    def post(self, request):
        """
        删除用例集
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        obj = check_object(data, request)
        if obj:
            return obj
        plan = TestPlan.objects.filter(id__in=data.get('idList'), status=True)
        if plan.count() != len(data.get('idList')):
            return JsonResponse(code='999995', msg='参数idList中含有不存在的or已禁用or重复的id')
        plan.update(status=False, updateTime=datetime.now(), updateUser=request.user.pk)
        record_dynamic(project=data.get('project_id'),
                       _type='删除', operationObject='测试计划', user=request.user.pk,
                       data=f"删除测试计划{data.get('idList')}")
        return JsonResponse(code='999999', msg='success', data={'idList': data.get('idList')})


class ImportPlanCase(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['planId', 'addList', 'deleteList', 'updateList']:
            try:
                data[param]
            except KeyError:
                raise ParamsMissedException(param)
        for param in ['addList', 'deleteList', 'updateList']:
            if data.get(param):
                if not isinstance(data.get(param), list):
                    raise ParamsTypeErrorException(param)
                if param == 'addList':
                    for case in data.get(param):
                        if not isinstance(case, dict):
                            raise ParamsTypeErrorException('addList列表参数')
                        if sorted(case.keys()) != sorted(['caseId', 'index']):
                            raise ParamsTypeErrorException('addList列表参数')
                elif param == 'updateList':
                    for case in data.get(param):
                        if not isinstance(case, dict):
                            raise ParamsTypeErrorException('updateList列表参数')
                        if sorted(case.keys()) != sorted(['planCaseId', 'index']):
                            raise ParamsTypeErrorException('updateList列表参数')
                elif param == 'deleteList':
                    for case in data.get(param):
                        if not isinstance(case, int):
                            raise ParamsTypeErrorException('deleteList列表参数类型错误')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['planId', 'addList'],
        properties={
            'planId': openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划id'),
            'addList': openapi.Schema(type=openapi.TYPE_ARRAY,
                                      items=openapi.Schema(type=openapi.TYPE_OBJECT, required=['index', 'caseId'],
                                                           properties={
                                                               'index': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                                                       description='用例执行顺序index'),
                                                               'caseId': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                                                        description='用例id')
                                                           }),
                                      description='导入用例列表'),
            'deleteList': openapi.Schema(type=openapi.TYPE_ARRAY,
                                         items=openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划用例id')),
            'updateList': openapi.Schema(type=openapi.TYPE_ARRAY,
                                         items=openapi.Schema(type=openapi.TYPE_OBJECT, required=['index', 'caseId'],
                                                              properties={
                                                                  'index': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                                                          description='用例执行顺序index'),
                                                                  'planCaseId': openapi.Schema(
                                                                      type=openapi.TYPE_INTEGER,
                                                                      description='测试计划用例id')
                                                              },
                                                              description='修改用例列表'))}
    ))
    @catch_exception
    def post(self, request):
        """
        导入测试用例
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        plan_id = data.get('planId')
        add_list = data.get('addList')
        delete_list = data.get('deleteList')
        update_list = data.get('updateList')
        plan = TestPlan.objects.filter(id=plan_id)
        if not plan:
            raise ObjectNotFoundException(f'测试计划{plan_id}')
        if not plan.filter(status=True):
            raise ObjectIsDeletedException(f'测试计划{plan_id}')
        # 测试计划删除用例
        if not add_list and not delete_list and not update_list:
            raise ParamsMissedException('addList', 'deleteList', 'updateList')
        if delete_list:
            delete_obj = TestPlanCase.objects.filter(id__in=delete_list)
            if delete_obj.count() < len(delete_list):
                raise ObjectNotFoundException('deleteList中id')
            if delete_obj.filter(status=True).count() < len(delete_list):
                raise ObjectIsDeletedException('deleteList中id')
        # 测试用例计划导入新用例
        if add_list:
            new_case_list = [case.get('caseId') for case in add_list]
            case_obj = ApiCaseInfo.objects.filter(id__in=set(new_case_list))
            if case_obj.count() < len(set(new_case_list)):
                raise ObjectNotFoundException('addList列表中caseId')
            if case_obj.filter(status=True).count() < len(set(new_case_list)):
                raise ObjectIsDeletedException('addList列表中caseId')
            case_info_list = []
            for case in case_obj:
                for i in add_list:
                    if i.get('caseId') == case.id:
                        api = ApiInfo.objects.get(id=case.api_id)
                        header = {}
                        if len(eval(case.header)) > 0:
                            for h in eval(case.header):
                                h.pop('_XID')
                                header[f'{h["key"]}'] = h["value"]
                        case_info_list.append({'index': i['index'], 'name': case.name, 'description': case.description,
                                               'request': json.dumps({'url': api.apiAddress, 'body': eval(case.data),
                                                                      'header': header, 'method': api.requestType
                                                                      }), 'case_id': case.id,
                                               'responseAssert': "[]",
                                               'globalVariable': "[]"
                                               })
        # 测试计划更新用例执行index
        if update_list:
            plan_case_id_list = [case.get('planCaseId') for case in update_list]
            plan_case_index_list = [case.get('index') for case in update_list]
            if len(set(plan_case_id_list)) < len(plan_case_id_list):
                return JsonResponse(code='999995', msg='updateList中存在重复的planCaseId!')
            if len(set(plan_case_index_list)) < len(plan_case_index_list):
                return JsonResponse(code='999995', msg='updateList中存在重复的index!')
            plan_case_obj = TestPlanCase.objects.filter(id__in=plan_case_id_list, plan_id=plan_id)
            if plan_case_obj.count() < len(plan_case_id_list):
                raise ObjectNotFoundException('updateList中planCaseId')
        with transaction.atomic():
            if add_list:
                case_serialize = TestPlanCaseDeserializer(data=case_info_list, many=True)
                if case_serialize.is_valid():
                    if delete_list:
                        delete_obj.delete()
                    case_serialize.save(plan=TestPlan.objects.get(id=plan_id),
                                        createUser=User.objects.get(id=request.user.pk),
                                        updateUser=User.objects.get(id=request.user.pk))
                    if update_list:
                        db_index = [obj.index for obj in TestPlanCase.objects.exclude(id__in=plan_case_id_list
                                                                                      ).filter(plan_id=plan_id)]
                        if list(set(db_index).intersection(set(plan_case_index_list))):
                            return JsonResponse(code='999995', msg='updateList列表与测试计划其他用例index重复!')
                        for pid in update_list:
                            obj = TestPlanCase.objects.filter(id=pid.get('planCaseId'))
                            obj.update(index=pid.get('index'), updateTime=datetime.now(), updateUser=request.user.pk)
                else:
                    return JsonResponse(msg=case_serialize.errors, code='999995')
            else:
                if delete_list:
                    delete_obj.delete()
                if update_list:
                    db_index = [obj.index for obj in TestPlanCase.objects.exclude(id__in=plan_case_id_list
                                                                                  ).filter(plan_id=plan_id)]
                    if list(set(db_index).intersection(set(plan_case_index_list))):
                        return JsonResponse(code='999995', msg='updateList列表与测试计划其他用例index重复!')
                    for pid in update_list:
                        obj = TestPlanCase.objects.filter(id=pid.get('planCaseId'))
                        obj.update(index=pid.get('index'), updateTime=datetime.now(), updateUser=request.user.pk)
            serialize = TestPlanCaseSerializer(TestPlanCase.objects.filter(plan_id=plan_id,
                                                                           status=True).order_by('index'), many=True)
            return JsonResponse(data={'planId': plan_id, 'caseList': serialize.data},
                                code='999999', msg='成功！')


class ExecuteTestPlan(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        if not data.get('planId'):
            raise ParamsMissedException('planId')
        if not isinstance(data.get('planId'), list):
            raise ParamsTypeErrorException('planId')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['planId'],
        properties={
            'planId': openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划id')
        },
    ))
    @catch_exception
    def generate_robot_file(self, plan_id):
        plan = TestPlan.objects.filter(id=plan_id)
        if not plan:
            raise ObjectNotFoundException(f'测试计划{plan_id}')
        if plan.filter(status=False):
            raise ObjectIsDeletedException(f'测试计划{plan_id}')
        case = TestPlanCase.objects.filter(plan_id=plan_id).order_by('index')
        index_list = [i.index for i in case]
        if len(set(index_list)) < len(index_list):
            return JsonResponse(code='999995', msg=f'测试计划:{plan[0].name}用例存在重复index!')
        if not case:
            return JsonResponse(code='999995', msg=f'测试计划:{plan[0].name}下无用例,请添加用例!')
        if not case.filter(status=True):
            return JsonResponse(code='999995', msg=f'测试计划:{plan[0].name}下无用例,请添加用例!')
        serializer = TestPlanCaseSerializer(case, many=True)
        case_list = serializer.data
        const.generate_robot_file(case_list, plan[0].name, plan[0].id)

    @catch_exception
    def post(self, request):
        """
        执行测试计划
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        plan_list = data.get('planId')
        plan_obj = TestPlan.objects.filter(id__in=plan_list)
        if not plan_obj:
            raise ObjectNotFoundException(f'测试计划列表{plan_list}')
        if len(plan_list) > len(plan_obj):
            db_plan_list = [int(obj.id) for obj in plan_obj]
            not_exist_plan = list(set(db_plan_list) ^ set(plan_list))
            not_exist_plan_list = [obj.name for obj in TestPlan.objects.filter(id__in=not_exist_plan)]
            raise ObjectNotFoundException(f'测试计划{not_exist_plan_list}')
        plan_obj_status = plan_obj.filter(status=False)
        if plan_obj_status:
            delete_plan = [obj.name for obj in plan_obj_status]
            raise ObjectIsDeletedException(f'测试计划{",".join(delete_plan)}')
        for plan_id in plan_list:
            name = TestPlan.objects.get(id=plan_id).name
            case = TestPlanCase.objects.filter(plan_id=plan_id).order_by('index')
            index_list = [i.index for i in case]
            if len(set(index_list)) < len(index_list):
                return JsonResponse(code='999995', msg=f'测试计划:{name}用例存在重复index!')
            if not case:
                return JsonResponse(code='999995', msg=f'测试计划:{name}下无用例,请添加用例!')
            if not case.filter(status=True):
                return JsonResponse(code='999995', msg=f'测试计划:{name}下无用例,请添加用例!')
        execute_info = ExecutePlanInfo.objects.all()
        if not execute_info:
            ExecutePlanInfo.objects.create(is_running=False)
        else:
            if execute_info[0].is_running:
                if execute_info[0].start_time >= execute_info[0].end_time:
                    execute_info.update(is_running=True, start_time=datetime.now())
                else:
                    return JsonResponse(code='999995', msg='存在执行中的测试计划,请稍后重试!')
            else:
                execute_info.update(is_running=True, start_time=datetime.now())
        plan_executor = Run(plan_list)
        for plan_id in plan_list:
            self.generate_robot_file(plan_id)
        plan_executor.main()
        execute_info.update(is_running=False, end_time=datetime.now())
        test_data = DbPlanCase().select_all_data()
        response_list = []
        for plan_id in plan_list:
            plan_name = TestPlan.objects.get(id=plan_id).name
            response = {'planId': plan_id, 'planName': plan_name,
                        'caseList': sorted([{'caseName': TestPlanCase.objects.get(plan_id=plan_id,
                                                                                  index=int(data.api_name
                                                                                            .split('Case')[-1])).name,
                                             'index': int(data.api_name.split('Case')[-1]),
                                             'request': json.loads(data.req_json), 'success': data.is_success,
                                             'response': json.loads(data.resp_json) if data.resp_json else {}}
                                            for data in test_data if
                                            int(data.api_name.split('_')[0].split('TestPlan')[-1]) == int(plan_id)],
                                           key=lambda x: x['index'])}
            response_list.append(response)
        return JsonResponse(code='999999', msg='success', data=response_list)


class TestPlanGlobalParameter(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(name='planCaseId', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='测试计划用例id'),
    ])
    @catch_exception
    def get(self, request):
        """
        获取测试计划可用变量
        """
        case_id = request.GET.get('planCaseId')
        if not case_id:
            raise ParamsMissedException('planCaseId')
        case_obj = TestPlanCase.objects.filter(id=case_id)
        if not case_obj:
            raise ObjectNotFoundException(f'planCaseId:{case_id}')
        if case_obj.filter(status=False):
            raise ObjectIsDeletedException(f'planCaseId:{case_id}')
        case_list = TestPlanCase.objects.filter(index__lt=case_obj[0].index,
                                                plan_id=case_obj[0].plan_id).order_by('index')
        param_list = []
        for case in case_list:
            if case.globalVariable:
                for param in eval(case.globalVariable):
                    param_list.append(param.get('key'))
        config = TestPlanConf.objects.filter(plan_id=case_obj[0].plan_id)
        params = json.loads(config[0].param) if config else {}
        if params:
            for key in params.keys():
                param_list.append(key)
        param_dict = {}
        for param in set(param_list):
            param_dict[param] = '${' + param + '}'
        return JsonResponse(code='999999', msg='success', data={"params": param_dict})


class TestPlanReportList(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(name='planId', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='测试计划id'),
        openapi.Parameter(name='reportId', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='测试计划报告id'),
    ])
    @catch_exception
    def get(self, request):
        """
        获取测试计划执行结果
        """
        try:
            page_size = int(request.GET.get('page_size', 20))
            current_page = int(request.GET.get('page', 1))
        except (TypeError, ValueError):
            return JsonResponse(code='999985', msg='page and page_size must be integer！')
        plan_id = request.GET.get('planId')
        report_id = request.GET.get('reportId')
        if not plan_id:
            raise ParamsMissedException('planId')
        if not report_id:
            report_obj = TestPlanExecuteCaseReport.objects.filter(plan_id=plan_id).defer('report').order_by('-end_time')
            if not report_obj:
                return JsonResponse(code='999999', msg='success', data={'reportList': []})
            paginator = Paginator(report_obj, page_size)  # paginator对象
            page_sizes = paginator.num_pages  # 总页数
            total = paginator.count  # 总数
            try:
                obm = paginator.page(current_page)
            except PageNotAnInteger:
                obm = paginator.page(1)
            except EmptyPage:
                obm = paginator.page(paginator.num_pages)
            serializer = TestPlanExecuteCaseReportListSerializer(obm, many=True)
            return JsonResponse(data={'reportList': serializer.data,
                                      'currentPage': current_page,
                                      'total': total,
                                      'pageSizes': page_sizes,
                                      }, code='999999', msg='成功！')
        report_obj = TestPlanExecuteCaseReport.objects.get(plan_id=plan_id, id=report_id)
        if not report_obj:
            raise ObjectNotFoundException('report_id')
        serializer = TestPlanExecuteCaseReportSerializer(report_obj)
        html = serializer.data.get('report')
        return HttpResponse(html)


class AddTestPlanConfig(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['planId', 'env', 'param']:
            try:
                data[param]
            except KeyError:
                raise ParamsMissedException(param)
        if not data.get('planId'):
            raise ParamsIsNullException('planId')
        if not data.get('env') and not data.get('param'):
            return JsonResponse(code='999995', msg='参数env,param不能同时为空')
        for param in ['env', 'param']:
            if data.get(param):
                if not isinstance(data.get(param), dict):
                    raise ParamsTypeErrorException(param)
                data[param] = json.dumps(data[param])
            else:
                data[param] = json.dumps({})

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['planId'],
        properties={
            'planId': openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划id'),
            'env': openapi.Schema(type=openapi.TYPE_OBJECT,
                                  properties={'http': openapi.Schema(type=openapi.TYPE_STRING,
                                                                     description='http接口测试地址'),
                                              'websocket': openapi.Schema(type=openapi.TYPE_STRING,
                                                                          description='websocket接口测试地址')},
                                  description='全局环境'),
            'param': openapi.Schema(type=openapi.TYPE_OBJECT, description='全局变量')
        },
    ))
    @catch_exception
    def post(self, request):
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        plan_id = data.get('planId')
        plan_obj = TestPlan.objects.filter(id=plan_id)
        if not plan_obj:
            raise ObjectNotFoundException(f'测试计划{plan_id}')
        if not plan_obj.filter(status=True):
            raise ObjectIsDeletedException(f'测试计划{plan_id}')
        if TestPlanConf.objects.filter(plan_id=plan_id):
            return JsonResponse(code='999995', msg=f'测试计划{plan_id}配置已存在!')
        serializer = TestPlanConfDeserializer(data=data)
        if serializer.is_valid():
            serializer.save(plan=TestPlan.objects.get(id=plan_id),
                            createUser=User.objects.get(id=request.user.pk),
                            createTime=datetime.now())
            return JsonResponse(code='999999', msg='success', data=serializer.data)
        return JsonResponse(code='999995', msg=serializer.errors)


class UpdateTestPlanConfig(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        """
        校验参数
        """
        for param in ['id', 'env', 'param']:
            try:
                data[param]
            except KeyError:
                raise ParamsMissedException(param)
        if not data.get('id'):
            raise ParamsIsNullException('id')
        for param in ['env', 'param']:
            if data.get(param):
                if not isinstance(data.get(param), dict):
                    raise ParamsTypeErrorException(param)
                data[param] = json.dumps(data[param])
            else:
                data[param] = json.dumps({})

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['planId'],
        properties={
            'planId': openapi.Schema(type=openapi.TYPE_INTEGER, description='测试计划配置id'),
            'env': openapi.Schema(type=openapi.TYPE_OBJECT,
                                  properties={'http': openapi.Schema(type=openapi.TYPE_STRING,
                                                                     description='http接口测试地址'),
                                              'websocket': openapi.Schema(type=openapi.TYPE_STRING,
                                                                          description='websocket接口测试地址')},
                                  description='全局环境'),
            'param': openapi.Schema(type=openapi.TYPE_OBJECT, description='全局变量')

        },
    ))
    @catch_exception
    def post(self, request):
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        config_id = data.get('id')
        config_obj = TestPlanConf.objects.filter(id=config_id)
        if not config_obj:
            raise ObjectNotFoundException(f'测试计划配置{config_id}')
        serializer = TestPlanConfDeserializer(data=data)
        if serializer.is_valid():
            serializer.update(instance=config_obj[0], validated_data=data)
            config_obj.update(updateUser=request.user.pk)
            config = TestPlanConfSerializer(TestPlanConf.objects.filter(id=config_id)[0])
            return JsonResponse(code='999999', msg='success', data=config.data)
        return JsonResponse(code='999995', msg=serializer.errors)


class GetTestPlanConfig(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(name='planId', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description='测试计划id')
    ])
    @catch_exception
    def get(self, request):
        """
        获取测试计划配置信息
        """
        plan_id = request.GET.get('planId')
        if not plan_id:
            raise ParamsMissedException('planId')
        config_obj = TestPlanConf.objects.filter(plan_id=plan_id)
        if not config_obj:
            return JsonResponse(code='999999', msg='success', data={'param': {}, 'env': {}})
        serializer = TestPlanConfSerializer(config_obj[0])
        return JsonResponse(code='999999', msg='success', data=serializer.data)


